#pragma once

#include "ly/export.hpp"
#include "ly/manager.hpp"

namespace ly {

class Manager;
class LY_API Object : public std::enable_shared_from_this<Object> {
 public:
  typedef std::shared_ptr<Object> object_sptr;
  typedef std::shared_ptr<Op> op_sptr;
  typedef std::shared_ptr<Manager> manager_sptr;
  typedef std::weak_ptr<Manager> manager_wptr;

  Object() {};

  Object(manager_wptr manager) : m_id(0) {};

  Object(const Object& d) { manager(d.manager()); };

  virtual ~Object() {};

  template <typename... Args>
  static object_sptr create(Args... args) {
    return std::make_shared<Object>(args...);
  }

  /**
   *  @brief Manager object retrieval
   *
   *  Obtain the pointer to the management object this
   *  object is attached to. Returns 0 if not attached to a manager.
   */
  manager_sptr manager() const { return mp_manager.lock(); }

  /**
   *  @brief Attach to a different manager or detach
   *
   *  Changes the attachment of the object to any
   *  manager. Pass 0 to detach it from any manager.
   */
  void manager(manager_sptr p_manager);

  /** 
   *  @brief The undo 'strategy'
   *
   *  For a detailed description of this method see Manager::queue.
   *  This method has been declared to be no-throwing since it is
   *  assumed that once the operation is successfully queued it can be undone
   *  in every case.
   */
  virtual void undo(op_sptr /*op*/) {}

  /** 
   *  @brief The redo 'strategy'
   *
   *  For a detailed description of this method see Manager::queue.
   *  This method has been declared to be no-throwing since it is
   *  assumed that once the operation is successfully queued it can be redone
   *  in every case.
   */
  virtual void redo(op_sptr /*op*/) {}

  /**
   *  @brief The id getter method
   */
  Manager::ident_t id() const { return m_id; }

  /**
   *  @brief A convenience function to determine if we are transacting
   */
  bool transacting() const { return manager() && manager()->transacting(); }

  /**
   *  @brief A convenience function to determine if we are in an undo or redo replay operation
   */
  bool replaying() const { return manager() && manager()->replaying(); }

  /**
   *  @brief Begins a transaction (same as calling manager ()->transaction (), but safe against null manager ())
   */
  void transaction(const std::string& description, Manager::transaction_id_t join_with = 0) {
    if (manager()) {
      manager()->transaction(description, join_with);
    }
  }

  /**
   *  @brief Ends a transaction (same as calling manager ()->commit (), but safe against null manager ())
   */
  void commit() {
    if (manager()) {
      manager()->commit();
    }
  }

 private:
  Manager::ident_t m_id;
  manager_wptr mp_manager;

  //  The assignment operator is private in order to
  //  force a specific treatment of the manager object attachment
  Object& operator=(const Object&);
};
}  // namespace ly
